using System;
using System.IO;

namespace System.Windows.Media.DirectShow.Vorbis
{

    public abstract class Mapping
    {

        public static Mapping CreateInstance(VorbisStream vorbis, IBitStream source, SetupHeader header)
        {

            int type = source.ReadInt32(16);
            switch (type)
            {
                case 0:
                    //System.out.println("mapping type 0");
                    return new Mapping0(vorbis, source, header);
                default:
                    throw new VorbisFormatException("Mapping type " + type + " is not supported.");
            }
        }

        public abstract int Type { get; }
        public abstract int[] Angles { get; }
        public abstract int[] Magnitudes { get; }
        public abstract int[] Mux { get; }
        public abstract int[] SubmapFloors { get; }
        public abstract int[] SubmapResidues { get; }
        public abstract int CouplingSteps { get; }
        public abstract int Submaps { get; }
    }

    public class Mapping0 : Mapping
    {

        private int[] magnitudes, angles, mux, submapFloors, submapResidues;

        public Mapping0(VorbisStream vorbis, IBitStream source, SetupHeader header)
        {

            int submaps = 1;

            if (source.ReadBit())
            {
                submaps = source.ReadInt32(4) + 1;
            }

            //System.out.println("submaps: "+submaps);

            int channels = vorbis.IdentificationHeader.Channels;
            uint ilogChannels = Util.Ilog(channels - 1);

            //System.out.println("ilogChannels: "+ilogChannels);

            if (source.ReadBit())
            {
                int couplingSteps = source.ReadInt32(8) + 1;
                magnitudes = new int[couplingSteps];
                angles = new int[couplingSteps];

                for (int i = 0; i < couplingSteps; i++)
                {
                    magnitudes[i] = source.ReadInt32(ilogChannels);
                    angles[i] = source.ReadInt32(ilogChannels);
                    if (magnitudes[i] == angles[i] || magnitudes[i] >= channels || angles[i] >= channels)
                    {
                        throw new VorbisFormatException("The channel magnitude and/or angle mismatch.");
                    }
                }
            }
            else
            {
                magnitudes = new int[0];
                angles = new int[0];
            }

            if (source.ReadInt32(2) != 0)
            {
                throw new VorbisFormatException("A reserved mapping field has an invalid value.");
            }

            mux = new int[channels];
            if (submaps > 1)
            {
                for (int i = 0; i < channels; i++)
                {
                    mux[i] = source.ReadInt32(4);
                    if (mux[i] > submaps)
                    {
                        throw new VorbisFormatException("A mapping mux value is higher than the number of submaps");
                    }
                }
            }
            else
            {
                for (int i = 0; i < channels; i++)
                {
                    mux[i] = 0;
                }
            }

            submapFloors = new int[submaps];
            submapResidues = new int[submaps];

            int floorCount = header.Floors.Length;
            int residueCount = header.Residues.Length;

            for (int i = 0; i < submaps; i++)
            {
                source.ReadInt32(8); // discard time placeholder
                submapFloors[i] = source.ReadInt32(8);
                submapResidues[i] = source.ReadInt32(8);

                if (submapFloors[i] > floorCount)
                {
                    throw new VorbisFormatException("A mapping floor value is higher than the number of floors.");
                }

                if (submapResidues[i] > residueCount)
                {
                    throw new VorbisFormatException("A mapping residue value is higher than the number of residues.");
                }
            }
        }

        public override int Type
        {
            get
            {
                return 0;
            }
        }

        public override int[] Angles
        {
            get
            {
                return angles;
            }
        }

        public override int[] Magnitudes
        {
            get
            {
                return magnitudes;
            }
        }

        public override int[] Mux
        {
            get
            {
                return mux;
            }
        }

        public override int[] SubmapFloors
        {
            get
            {
                return submapFloors;
            }
        }

        public override int[] SubmapResidues
        {
            get
            {
                return submapResidues;
            }
        }

        public override int CouplingSteps
        {
            get
            {
                return angles.Length;
            }
        }

        public override int Submaps
        {
            get
            {
                return submapFloors.Length;
            }
        }

    }
}
